package com.gel.codegeneration.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.gel.codegeneration.ai.AiCodeGenTypeRoutingService;
import com.gel.codegeneration.ai.AiCodeGenTypeRoutingServiceFactory;
import com.gel.codegeneration.ai.model.enums.CodeGenTypeEnum;
import com.gel.codegeneration.common.constant.AppConstant;
import com.gel.codegeneration.common.enums.ChatHistoryMessageTypeEnum;
import com.gel.codegeneration.common.enums.ErrorCode;
import com.gel.codegeneration.convention.exception.BusinessException;
import com.gel.codegeneration.core.AiCodeGeneratorFacade;
import com.gel.codegeneration.core.builder.VueProjectBuilder;
import com.gel.codegeneration.core.handler.StreamHandlerExecutor;
import com.gel.codegeneration.model.dto.req.app.AppAddRequest;
import com.gel.codegeneration.model.dto.req.app.AppQueryRequest;
import com.gel.codegeneration.model.dto.resp.app.AppVO;
import com.gel.codegeneration.model.dto.resp.user.UserVO;
import com.gel.codegeneration.model.entity.User;
import com.gel.codegeneration.service.ChatHistoryService;
import com.gel.codegeneration.service.ScreenshotService;
import com.gel.codegeneration.service.UserService;
import com.gel.codegeneration.utils.ThrowUtils;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.gel.codegeneration.model.entity.App;
import com.gel.codegeneration.mapper.AppMapper;
import com.gel.codegeneration.service.AppService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;

import java.io.File;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 应用 服务层实现。
 *
 * @author <a>gel</a>
 */
@Service
@Slf4j
public class AppServiceImpl extends ServiceImpl<AppMapper, App>  implements AppService{
    @Resource
    private UserService userService;
    @Resource
    private AiCodeGeneratorFacade aiCodeGeneratorFacade;
    @Resource
    @Lazy
    private ChatHistoryService chatHistoryService;
    @Resource
    private StreamHandlerExecutor streamHandlerExecutor;
    @Resource
    private VueProjectBuilder vueProjectBuilder;

    @Resource
    private ScreenshotService screenshotService;
    @Resource
    private AiCodeGenTypeRoutingServiceFactory aiCodeGenTypeRoutingServiceFactory;

    @Override
    public Long createApp(AppAddRequest appAddRequest, User loginUser) {
        // 参数校验
        String initPrompt = appAddRequest.getInitPrompt();
        ThrowUtils.throwIf(StrUtil.isBlank(initPrompt), ErrorCode.PARAMS_ERROR, "初始化 prompt 不能为空");
        // 构造入库对象
        App app = new App();
        BeanUtil.copyProperties(appAddRequest, app);
        app.setUserId(loginUser.getId());
        // 应用名称暂时为 initPrompt 前 12 位
        app.setAppName(initPrompt.substring(0, Math.min(initPrompt.length(), 12)));
        // 使用 AI 智能选择代码生成类型
        AiCodeGenTypeRoutingService aiCodeGenTypeRoutingService = aiCodeGenTypeRoutingServiceFactory.createAiCodeGenTypeRoutingService();
        CodeGenTypeEnum selectedCodeGenType = aiCodeGenTypeRoutingService.routeCodeGenType(initPrompt);
        app.setCodeGenType(selectedCodeGenType.getValue());
        // 插入数据库
        boolean result = this.save(app);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
        log.info("应用创建成功，ID: {}, 类型: {}", app.getId(), selectedCodeGenType.getValue());
        return app.getId();
    }

    @Override
    public AppVO getAppVO(App app) {
        if(app == null){
            return null;
        }
        AppVO appVO = BeanUtil.toBean(app, AppVO.class);
        Long userId = app.getUserId();
        if(userId != null){
            User user = userService.getById(userId);
            UserVO useVO = userService.getUseVO(user);
            appVO.setUser(useVO);
        }
        return appVO;


    }

    @Override
    public QueryWrapper getQueryWrapper(AppQueryRequest appQueryRequest) {
        ThrowUtils.throwIf(appQueryRequest == null, ErrorCode.PARAMS_ERROR);



        QueryWrapper queryWrapper = QueryWrapper.create()
                .eq(App::getId, appQueryRequest.getId())
                .like(App::getAppName, appQueryRequest.getAppName())
                .like(App::getCover, appQueryRequest.getCover())
                .like(App::getInitPrompt, appQueryRequest.getInitPrompt())
                .eq(App::getCodeGenType, appQueryRequest.getCodeGenType())
                .eq(App::getDeployKey, appQueryRequest.getDeployKey())
                .eq(App::getPriority, appQueryRequest.getPriority())
                .orderBy(appQueryRequest.getSortField(), "ascend".equals(appQueryRequest.getSortOrder()));
        return queryWrapper;


    }

    @Override
    public List<AppVO> getListAppVO(List<App> appList) {
        if(appList.isEmpty()){
            return new ArrayList<>();
        }

        Set<Long> userIds = appList.stream()
                .map(App::getUserId)
                .collect(Collectors.toSet());
        List<User> users = userService.listByIds(userIds);
        Map<Long, UserVO> userVOMap = users.stream()
                .collect(Collectors.toMap(User::getId, userService::getUseVO));
        List<AppVO> appVOList = appList.stream()
                .map(app -> {
                    AppVO appVO = getAppVO(app);
                    appVO.setUser(userVOMap.get(app.getUserId()));
                    return appVO;
                }).collect(Collectors.toList());
        return appVOList;
    }

    @Override
    public Flux<String> chatToGenCode(Long appId, String userMessage, User loginUser) {
        //参数校验
        ThrowUtils.throwIf(appId == null || appId <= 0, ErrorCode.PARAMS_ERROR, "应用 ID 错误");
        ThrowUtils.throwIf(StrUtil.isBlank(userMessage), ErrorCode.PARAMS_ERROR, "提示词不能为空");
        //查询应用消息
        App app = this.getById(appId);
        ThrowUtils.throwIf(app == null, ErrorCode.NOT_FOUND_ERROR, "应用不存在");
        // 3. 权限校验，仅本人可以和自己的应用对话
        if (!app.getUserId().equals(loginUser.getId())) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "无权限访问该应用");
        }
        String codeGenType = app.getCodeGenType();
        CodeGenTypeEnum enumByValue = CodeGenTypeEnum.getEnumByValue(codeGenType);
        if(enumByValue == null){
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "应用代码生成类型错误");
        }
        //添加用户消息到对话历史
        chatHistoryService.addChatMessage(appId,userMessage, ChatHistoryMessageTypeEnum.USER.getValue(), loginUser.getId());
        //调用ai生成代码
        //获取输出流
        Flux<String> contentFlux = aiCodeGeneratorFacade.generateAndSaveCodeStream(userMessage, enumByValue, appId);
        //收集ai响应并持久化
        return streamHandlerExecutor.doExecute(contentFlux, chatHistoryService, appId, loginUser, enumByValue);



    }

    @Override
    public String deploy(Long appId, User loginUser) {
        //参数校验
        ThrowUtils.throwIf(appId == null || appId <= 0, ErrorCode.PARAMS_ERROR, "应用 ID 错误");
        ThrowUtils.throwIf(loginUser == null, ErrorCode.NOT_LOGIN_ERROR, "用户未登录");
        // 2. 查询应用信息
        App app = this.getById(appId);
        ThrowUtils.throwIf(app == null, ErrorCode.NOT_FOUND_ERROR, "应用不存在");
        //验证用户是否有权限部署
        Long userId = loginUser.getId();
        if (!app.getUserId().equals(userId)){
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "无权限部署该应用");
        }
        //检查是否已有部署标识
        String deployKey = app.getDeployKey();
        if (StrUtil.isBlank(deployKey)){
            deployKey = RandomUtil.randomString(6);
        }
        //获取代码生成类型，构建资源目录路径
        String codeGenType = app.getCodeGenType();
        String sourceDirName=codeGenType+"_"+appId;
        String sourceDirPath = AppConstant.CODE_OUTPUT_ROOT_DIR+ File.separator+sourceDirName;
        //检查路径是否存在
        File sourceDir=  new File(sourceDirPath);
        if (!sourceDir.exists()||!sourceDir.isDirectory()) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "应用代码路径不存在，请先生成应用");
        }
        //vue项目特殊处理
        CodeGenTypeEnum enumByValue = CodeGenTypeEnum.getEnumByValue(codeGenType);
        if (enumByValue.equals(CodeGenTypeEnum.VUE_PROJECT)) {
            //构建项目
            boolean buildSuccess = vueProjectBuilder.buildProject(sourceDirPath);
            ThrowUtils.throwIf(!buildSuccess, ErrorCode.SYSTEM_ERROR, "构建 Vue 项目失败,请检查代码和依赖");
            //检查dist目录是否存在
            File distDir = new File(sourceDirPath, "dist");
            ThrowUtils.throwIf(!distDir.exists(), ErrorCode.SYSTEM_ERROR, "构建 Vue 项目成功但为生产dist目录");
            //将dist作为部署源
            sourceDir=distDir;
            log.info("Vue 项目构建成功，dist 目录: {}", distDir.getAbsolutePath());

        }
        //复制文件到部署路径
        String deployDirPath = AppConstant.CODE_DEPLOY_ROOT_DIR + File.separator + deployKey;
        try {
            FileUtil.copyContent(sourceDir,new File(deployDirPath),true);
        } catch (Exception e) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "应用部署失败：" + e.getMessage());
        }
        //更新deploykey与部署时间
        App updateApp = App.builder()
                .id(appId)
                .deployKey(deployKey)
                .deployedTime(LocalDateTime.now())
                .build();
        boolean updateResult = this.updateById(updateApp);
        if (!updateResult){
            ThrowUtils.throwIf(!updateResult, ErrorCode.OPERATION_ERROR, "更新应用部署信息失败");
        }
        //返回可访问的url
        String deployUrl = String.format("%s/%s/", AppConstant.CODE_DEPLOY_HOST, deployKey);
        //根据部署完的地址获取封面截图并持久化
        generateAppScreenshot(appId,deployUrl);
        return deployUrl;

    }
    /**
     * 生成应用截图
     * @param appId
     * @param appUrl
     */
    public void generateAppScreenshot(Long appId,String appUrl) {
        //获取应用截图Url
        String screenshotUrl= screenshotService.generateAndUploadScreenshot(appUrl);
        //更新数据库
        App updateApp = App.builder()
                .id(appId)
                .cover(screenshotUrl)
                .build();
        boolean updateResult = this.updateById(updateApp);
        ThrowUtils.throwIf(!updateResult, ErrorCode.OPERATION_ERROR, "更新应用封面失败");
    }

    //重写删除方法（关联到会话记忆删除）
    @Override
    public boolean removeById(Serializable id) {
        if(id == null){
            return false;
        }
        Long appId= Long.parseLong(id.toString());
        if(appId <=0){
            return false;
        }
        //删除关联的会话记录
        try {
            chatHistoryService.deleteByAppId(appId);
        } catch (Exception e) {
            log.error("删除应用关联的对话历史失败：{}", e.getMessage());
        }

        //删除应用
        return super.removeById(id);
    }

}
